#pragma once
#include <glm/glm.hpp>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QEasingCurve>
#include <QTimer>
#include "../../core/cbase.h"

#define SubDivisionMs 16 // 16 ms for approximately 60 FPS

struct QAnimProp {
	CString name;
	float startValue;
	float endValue;
	float currentValue;

	QAnimProp() = default;
	QAnimProp(const CString& name, float startValue, float endValue) {
		this->name = name;
		this->startValue = startValue;
		this->endValue = endValue;
	}
};

class QAnimator {
public:
	static void startWithValue(float startValue, float endValue, int durationMs, const std::function<void(float)>& callback, QEasingCurve::Type animType = QEasingCurve::Linear) {
		auto anim = new QAnimator(startValue, endValue, animType);
		anim->start(callback, durationMs);
	}

	static void startWidthVertex(const Vec3& startP, const Vec3& endP, int durationMs, const std::function<void(const Vec3&)>& callback, QEasingCurve::Type animType = QEasingCurve::Linear) {
		auto anim = new QAnimator(startP, endP, animType);
		anim->start(callback, durationMs);
	}

	static void startWith(const CVector<QAnimProp>& animProps, int durationMs, const std::function<void(const CVector<QAnimProp>&)>& callback, QEasingCurve::Type animType = QEasingCurve::Linear) {
		auto anim = new QAnimator(animProps, animType);
		anim->start(callback, durationMs);
	}

private:
	QAnimator() = default;

	QAnimator(float startValue, float endValue, QEasingCurve::Type animType) {
		QAnimProp prop("", startValue, endValue);
		m_animProps << prop;
		m_easingCurve.setType(animType);
	}

	QAnimator(const CVector<QAnimProp>& animProps, QEasingCurve::Type animType) {
		m_animProps = animProps;
		m_easingCurve.setType(animType);
	}

	QAnimator(const Vec3& startP, const Vec3& endP, QEasingCurve::Type animType) {
		QAnimProp propX("x", startP.x, endP.x);
		QAnimProp propY("y", startP.y, endP.y);
		QAnimProp propZ("z", startP.z, endP.z);
		m_animProps << propX << propY << propZ;
		m_easingCurve.setType(animType);
	}

	void start(const std::function<void(float)>& callback, int durationMs) {
		auto Func = [=](const CVector<QAnimProp>& props) {
			callback(props[0].currentValue);
		};
		start(Func, durationMs);
	}

	void start(const std::function<void(const Vec3&)>& callback, int durationMs) {
		auto Func = [=](const CVector<QAnimProp>& props) {
			Vec3 vertex;
			for (auto& prop : props) {
				if ("x" == prop.name)
					vertex.x = prop.currentValue;
				else if ("y" == prop.name)
					vertex.y = prop.currentValue;
				else if ("z" == prop.name)
					vertex.z = prop.currentValue;
			}
			callback(vertex);
		};
		start(Func, durationMs);
	}

	void start(const std::function<void(const CVector<QAnimProp>&)>& callback, int durationMs) {
		m_timer = new QTimer();
		m_timer->setInterval(SubDivisionMs);
		QObject::connect(m_timer, &QTimer::timeout, [&, durationMs, callback, this]() {
			m_elapsedMs += SubDivisionMs;
			float progress = float(m_elapsedMs) / durationMs;
			if (progress >= 1.0) {
				progress = 1.0;
				m_timer->stop();
				m_timer->deleteLater();
				m_timer = nullptr;
			}
			for (auto& prop : m_animProps)
				prop.currentValue = prop.startValue + (prop.endValue - prop.startValue) * m_easingCurve.valueForProgress(progress);

			callback(m_animProps);

			if (m_timer == nullptr)
				delete this;// must create this object by 'new'!
		});
		m_timer->start();
	}

	QEasingCurve m_easingCurve;
	QTimer* m_timer = nullptr;
	CVector<QAnimProp> m_animProps;
	int m_elapsedMs = 0;
};

/*use example:
	Animator::start(Vec3(0,1,2), Vec3(6, 6, 6), 2000,
					[](const Vec3& vertex) {
						LogD << vertex.x << ", " << vertex.y << ", " << vertex.z;
					});
	Animator::start([](float value) {
		LogD << value;
	}, 1.0, 2.0, 2000);

	CVector<QAnimProp> animProps;
	animProps << QAnimProp{"x", 0.0, 1.0} << QAnimProp{"y", 3.0, 6.0};
	Animator::start([](const CVector<QAnimProp>& props) {
		for (auto& prop : props)
			LogD << prop.name << ":" << prop.currentValue;
	}, animProps, 2000);*/